package org.discala.util.logging

import LogLevel._

/**
 * A custom logger implementation that tracks log messages and allows the number
 * and their contents to be asserted on. Provided to allow a convenient mechanism
 * for unit testing that code is writing the expected logging information. While
 * testing logging statements is not typical in many projects it is important in
 * frameworks such as this where developers learn and expect a consistent set of
 * useful logging information.
 *
 * @author Chris Turner
 */
trait AssertableLogger extends Logged with LoggerUtils {

  private var entries = List[LogEntry]()

  import LogLevel._

  def trace(message: String, values: Any*) = log(Trace, message, values.toList)

  def trace(message: String, error: Throwable) = log(Trace, message, error)

  def debug(message: String, values: Any*) = log(Debug, message, values.toList)

  def debug(message: String, error: Throwable) = log(Debug, message, error)

  def info(message: String, values: Any*) = log(Info, message, values.toList)

  def info(message: String, error: Throwable) = log(Info, message, error)

  def warn(message: String, values: Any*) = log(Warn, message, values.toList)

  def warn(message: String, error: Throwable) = log(Warn, message, error)

  def error(message: String, values: Any*) = log(Error, message, values.toList)

  def error(message: String, error: Throwable) = log(Error, message, error)

  def log(level: LogLevel, message: String, params: List[Any]) =
    entries = new LogEntry(level, parameteriseMessage(message, params)) :: entries

  def log(level: LogLevel, message: String, error: Throwable) =
    entries = new LogEntry(level, message, Some(error)) :: entries

  def has = new LogEntryMatcher(entries)
}

case class LogEntry(val level: LogLevel, val message: String, val cause: Option[Throwable]) {

  def this(level: LogLevel, message: String) = this(level, message, None)
}

class LogEntryMatcher(entries: List[LogEntry]) {

  private var expectedCount = 0

  def apply(count: Int) = {
    expectedCount = count
    this
  }

  def messages() = {
    if ( entries.size == expectedCount ) true
    else {
      println("Expected: " + expectedCount + " log entries, but found: " + entries.size)
      false
    }
  }

  def message() = messages()

  def trace() = this

  def debug() = this

  def info() = this

  def warn() = this

  def error() = this
}